package io.renren.utils;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.*;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 文件处理.
 *
 * @author dzl
 */
public class FileBaseUtil {

    private static Logger logger = LoggerFactory.getLogger(FileBaseUtil.class);


    public final static String SEPARATOR = File.separator;

    /**
     * 检测文件扩展名.
     *
     * @param filepath   文件路径(全路径或相对路径，包含文件名)
     * @param suffixList 允许的扩展名，多个用逗号分隔，如：jpg,gif
     * @return boolean, true_扩展名正确
     * @throws Exception the exception
     * @version
     */
    public static boolean checkFileSuffix(String filepath, String suffixList) throws Exception {
        if (suffixList.trim().equals("")) {
            // 允许的扩展名为空
            throw new Exception("suffixList is null.");
        }
        suffixList = "," + suffixList.trim().toLowerCase() + ",";
        String suffix = filepath.substring(filepath.lastIndexOf(".") + 1); // 扩展名
        suffix = "," + suffix.toLowerCase() + ",";
        if (suffixList.indexOf(suffix) < 0) {
            return false;
        }
        return true;
    }

    /**
     * 检测文件扩展名.
     *
     * @param file       文件
     * @param suffixList 允许的扩展名，多个用逗号分隔，如：jpg,gif
     * @return boolean, true_扩展名正确
     * @throws Exception the exception
     * @version
     */
    public static boolean checkFileSuffix(File file, String suffixList) throws Exception {
        String filepath = file.getAbsoluteFile().toString(); // 文件绝对路径
        return checkFileSuffix(filepath, suffixList);
    }


    /**
     * 检测文件大小.
     *
     * @param file    文件
     * @param maxsize 允许的最大值，单位:字节
     * @return boolean, true_大小在限制泛围内, 如果文件不存在或大小为0也返回false
     * @throws Exception the exception
     * @version
     */
    public static boolean checkFileSize(File file, long maxsize) throws Exception {
        long size = file.length(); // 文件大小
        if (size <= maxsize && size > 0) {
            return true;
        } else {
            return false;
        }
    }



    /**
     * 创建文件名，格式：前缀+时间yyyyMMddHHmmss+三位随机数+扩展名.
     *
     * @param prefix 前缀
     * @param suffix 扩展名，如：jpg
     * @param date   时间
     * @return String 文件名
     * @version
     */
    public static String createFilenameWithRandom(String prefix, String suffix, Date date) {
        StringBuffer sb = new StringBuffer();
        SimpleDateFormat formatDate = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
        sb.append(prefix);
        sb.append(formatDate.format(date));
        sb.append((int) (Math.random() * 1000));
        sb.append("." + suffix);
        return sb.toString();
    }

    /**
     * 创建文件名，格式：前缀+时间yyyyMMddHHmmss+扩展名.
     *
     * @param prefix 前缀
     * @param suffix 扩展名，如：jpg
     * @param date   时间
     * @return String 文件名
     * @version
     */
    public static String createFilename(String prefix, String suffix, Date date) {
        StringBuffer sb = new StringBuffer();
        SimpleDateFormat formatDate = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
        sb.append(prefix);
        sb.append(formatDate.format(date));
        sb.append("." + suffix);
        return sb.toString();
    }

    /**
     * 创建文件.
     *
     * @param path     文件所在的目录全路径
     * @param fileName 文件名
     * @return 文件
     * @throws Exception the exception
     * @version
     */
    public static File createFile(String path, String fileName) throws Exception {
        // 目录不存在时，先创建
        createFilePath(path);

        // 创建文件
        if (!path.endsWith(SEPARATOR)) {
            path += SEPARATOR;
        }
        File file = new File(path + fileName);
        file.createNewFile();
        return file;
    }

    /**
     * 创建文件.
     *
     * @param path 文件所在的目录全路径
     * @return 文件
     * @throws Exception the exception
     * @version
     */
    public static void createFilePath(String path) throws Exception {
        // 目录不存在时，先创建
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return;
    }

    /**
     * 删除文件.
     *
     * @param fullpath 文件全路径，含文件名
     * @version
     */
    public static void deleteFile(String fullpath) {
        File file = new File(fullpath);
        file.delete();
    }

    /**
     * 复制目录及目录下的内容.
     *
     * @param frompath 源目录全路径
     * @param topath   目标目录全路径
     * @throws Exception the exception
     * @version
     */
    public static void copyDirectory(String frompath, String topath) throws Exception {
        File from = new File(frompath);
        File to = new File(topath);
        if (from.exists()) {
            FileUtils.copyDirectory(from, to);
        }
    }

    /**
     * 复制文件.
     *
     * @param frompath 源文件全路径，含文件名
     * @param topath   目标文件全路径，含文件名
     * @throws Exception the exception
     * @version
     */
    public static void copyFile(String frompath, String topath) throws Exception {
        File from = new File(frompath);
        File to = new File(topath);
        if (from.exists()) {
            FileUtils.copyFile(from, to);
        }
    }

    /**
     * 保存文件
     *
     * @param stream   stream
     * @param path     全路径
     * @param filename 文件名
     * @throws Exception
     */
    public static void saveFile(InputStream stream, String path, String filename) throws Exception {
        createFile(path, filename);

        FileOutputStream fs = new FileOutputStream(path + "/" + filename);
        byte[] buffer = new byte[1024 * 1024];
        // int bytesum = 0;
        int byteread = 0;
        while ((byteread = stream.read(buffer)) != -1) {
            // bytesum += byteread;
            fs.write(buffer, 0, byteread);
            fs.flush();
        }
        fs.close();
        stream.close();
    }

    /**
     * 保存文件
     *
     * @param path     文件保存路径
     * @param fileName 文件名
     * @param file     byte数组文件
     * @throws IOException
     * @author jlchen
     */
    public static void saveFile(String path, String fileName, byte[] file) throws Exception {
        createFile(path, fileName);
        FileOutputStream fos = new FileOutputStream(path + "/" + fileName);

        fos.write(file);
        fos.close();
    }

    /**
     * 保存文件
     *
     * @param path 文件保存全路径，包括文件名
     * @param file byte数组文件
     * @throws IOException
     * @author jlchen
     */
    public static void saveFile(String path, byte[] file) throws Exception {
        FileOutputStream fos = new FileOutputStream(path);

        fos.write(file);
        fos.close();
    }

    /**
     * 文件转化为byte[]
     *
     * @param file file
     * @return byte[]
     */
    public static byte[] getBytes(File file) throws Exception {
        FileInputStream stream = new FileInputStream(file);
        ByteArrayOutputStream bs = new ByteArrayOutputStream(1024 * 1024 * 2); // 2M
        byte[] b = new byte[1024 * 1024];
        int n;
        while ((n = stream.read(b)) != -1) {
            bs.write(b, 0, n);
        }
        stream.close();
        bs.close();
        return bs.toByteArray();
    }

    /**
     * 获取文件后缀
     *
     * @param fileName fileName
     * @return String
     */
    public static String getFileSuffix(String fileName) {
        int index = fileName.lastIndexOf('.');
        String extName = fileName.substring(index + 1);
        return extName;
    }

    /**
     * 检测文件魔数
     * <p/>
     * <pre>
     * 文件魔数：很多类型的文件，起始的几个固定字节。用UtraEdit等工具的十六进制编辑模式，打开文件即可查看。
     * TXT文件没有魔数，因此检测的是文件内容。
     * </pre>
     *
     * @param file     文件
     * @param magicnum 魔数值
     * @return true_通过
     * @throws Exception
     * @author dzl 2015.09
     */
    public static boolean checkFileMagic(File file, String magicnum) throws Exception {
        FileInputStream filein = null;
        try {
            int length = magicnum.length() / 2;
            String fileHead = "";
            byte[] fileheader = new byte[length];
            filein = new FileInputStream(file);
            filein.read(fileheader, 0, length);

            for (int i = 0; i < length; i++) {
                String one = Integer.toHexString(fileheader[i] & 0xFF); // 转成16进制
                if (one.length() == 1) { // 补足2位
                    one = "0" + one;
                }
                fileHead += one;
                // System.out.print(one.toUpperCase() + " ");
            }
            // System.out.println("");
            if (magicnum.toUpperCase().equals(fileHead.toUpperCase())) {
                return true;
            }
        } catch (Exception e) {
            throw e;
        } finally {
            try {
                filein.close();
            } catch (Exception e) {
                throw e;
            }
        }
        return false;
    }

    /**
     * 检测文件魔数，根据扩展名
     *
     * @param file 文件
     * @param exts 文件扩展名，多个用逗号分隔。目前支持：wav, jpg, jpeg, gif, png
     * @return true_通过， false_不通过(不支持的)
     * @throws Exception
     * @author dzl 2015.09
     */
    public static boolean checkFileMagicByExt(File file, String exts) throws Exception {
        // 扩展名 --> 文件魔数
        HashMap map = new HashMap();
        map.put("wav", "52494646BE25000057415645"); // 只是验证WAV文件是否是波形编码，另外还有参数编码和混合编码
        map.put("jpg", "FFD8FF");
        map.put("jpeg", "FFD8FF");
        map.put("gif", "474946");
        map.put("png", "89504E");

        boolean hasExt = false; // 是否支持的扩展名
        boolean rst = false;
        String[] ext = exts.toLowerCase().split(",");
        for (int i = 0; i < ext.length; i++) {
            if (map.containsKey(ext[i])) {
                hasExt = true;
                rst = checkFileMagic(file, (String) map.get(ext[i]));
                if (rst) {
                    break;
                }
            }
        }
        if (!hasExt) { // 不支持的扩展名
            throw new Exception("checkFileMagicByExt: " + file.getName() + " | " + exts
                    + " --> The extension does not support.");
        }
        // System.out.println("checkFileMagicByExt: " + file.getName() + " | " +
        // exts + " --> " + rst);
        return rst;
    }


    /**
     * 以行为单位读取文件，常用于读面向行的格式化文件.
     *
     * @param filepath the filepath
     * @return the string
     * @version
     */
    public static String readFileByLines(String filepath) {
        InputStreamReader reader = null;
        BufferedReader bufferedReader = null;
        String text = "";
        try {
            logger.info("readFileByLines filepath : " + filepath);

            File file = new File(filepath);

            if (file.isFile() && file.exists()) { // 判断文件是否存在


                String code = codeString(filepath);
                if ("0".equals(code)) {
                    code = "utf-8";
                }

                logger.info("readFileByLines the charset in the file is: " + code);

                String tempString = null;
                reader = new InputStreamReader(new FileInputStream(file), code);// 考虑到编码格式
                bufferedReader = new BufferedReader(reader);
                // 一次读入一行，直到读入null为文件结束
                String lineSeparator = System.getProperty("line.separator", "\n");
                while ((tempString = bufferedReader.readLine()) != null) {
                    text = text + tempString + lineSeparator.toString();
                }

                if (text.lastIndexOf(lineSeparator.toString()) != -1) {
                    text = text.substring(0, text.lastIndexOf(lineSeparator.toString()));
                }

            } else {
                logger.error("readFileByLines can not find file, filePath=" + filepath);
                return null;
            }

        } catch (IOException e) {
            logger.error("readFileByLines error!", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines reader.close() error!", e);
                }
            }

            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines bufferedReader.close() error!", e);
                }
            }
        }
        return text;
    }


    /**
     * 以文件流的方式读取文件，springboot只能这样才能都去到jar的里面的文件
     *
     * @param resourcesfilepath 传递resources目录下的文件路径，springboot用
     *                          the filepath
     * @return the string
     * @version
     */
    public static String readFileByInputStream(String resourcesfilepath) {
        InputStreamReader reader = null;
        BufferedReader bufferedReader = null;
        String text = "";
        try {


            //兼容非springboot，通过路径读取文件
            String resourcesfilepathtemp = getClassPath() + resourcesfilepath;
            resourcesfilepathtemp = resourcesfilepathtemp.replace("//", "/");
            String content = readFileByLines(resourcesfilepathtemp);

            if (content != null) {
                return content;
            }

            String tempString = null;

            logger.info("readFileByInputStream path:" + resourcesfilepath);
            InputStream inputStream = FileBaseUtil.class.getClass().getResourceAsStream(resourcesfilepath);

            if (inputStream == null) {
                logger.error("readFileByInputStream error, FileBaseUtil.class.getClass() inputStream is null!");

                Resource source = new ClassPathResource(resourcesfilepath);
                inputStream = source.getInputStream();
                if (inputStream == null) {
                    logger.error("readFileByInputStream error, new ClassPathResource inputStream is null!");
                    return null;
                }
            }

            reader = new InputStreamReader(inputStream, "utf-8");// 考虑到编码格式
            bufferedReader = new BufferedReader(reader);
            // 一次读入一行，直到读入null为文件结束
            String lineSeparator = System.getProperty("line.separator", "\n");
            while ((tempString = bufferedReader.readLine()) != null) {
                text = text + tempString + lineSeparator.toString();
            }

            if (text.lastIndexOf(lineSeparator.toString()) != -1) {
                text = text.substring(0, text.lastIndexOf(lineSeparator.toString()));
            }

        } catch (IOException e) {
            logger.error("readFileByLines error!", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines reader.close() error!", e);
                }
            }

            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines bufferedReader.close() error!", e);
                }
            }
        }

        logger.info("readFileByInputStream content: {}", text);
        return text;
    }


    /**
     * 以行为单位读取文件，常用于读面向行的格式化文件.
     *
     * @param filepath the filepath
     * @return the string
     * @version
     */
    public static List<String> readFileLines(String filepath) {
        List<String> datalist = new ArrayList<String>();
        try {

            String code = codeString(filepath);
            if ("0".equals(code)) {
                code = "gbk";
            }

            datalist = readFileLines(filepath,code);

        } catch (IOException e) {
            logger.error("readFileByLines error!", e);
        }
        return datalist;
    }


    /**
     * 以行为单位读取文件，常用于读面向行的格式化文件.
     *
     * @param filepath the filepath
     * @return the string
     * @version
     */
    public static List<String> readFileLines(String filepath, String code) {
        InputStreamReader reader = null;
        BufferedReader bufferedReader = null;
        List<String> datalist = new ArrayList<String>();
        try {

            logger.info("readFileByLines filepath : " + filepath);

            File file = new File(filepath);

            if (file.isFile() && file.exists()) { // 判断文件是否存在
                String tempString = null;
                reader = new InputStreamReader(new FileInputStream(file), code);// 考虑到编码格式
                bufferedReader = new BufferedReader(reader);
                // 一次读入一行，直到读入null为文件结束
                String lineSeparator = System.getProperty("line.separator", "\n");
                while ((tempString = bufferedReader.readLine()) != null) {
                    datalist.add(tempString);
                }
            } else {
                logger.error("readFileByLines can not find file, filePath=" + filepath);
                return null;
            }

        } catch (IOException e) {
            logger.error("readFileByLines error!", e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines reader.close() error!", e);
                }
            }

            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    logger.error("readFileByLines bufferedReader.close() error!", e);
                }
            }
        }
        return datalist;
    }


    /**
     * 判断文件的编码格式.
     *
     * @param fileName :file
     * @return 文件编码格式
     * @throws IOException Signals that an I/O exception has occurred.
     * @version
     */
    public static String codeString(String fileName) throws IOException {
        BufferedInputStream bin = new BufferedInputStream(new FileInputStream(fileName));
        String code = null;
        try {
            int a = bin.read() << 8;
            int b = bin.read();
            int p = a + b;

            // 其中的 0xefbb、0xfffe、0xfeff、0x5c75这些都是这个文件的前面两个字节的16进制数
            switch (p) {
                case 0xefbb:
                    code = "UTF-8";
                    break;
                case 0xfffe:
                    code = "Unicode";
                    break;
                case 0xfeff:
                    code = "UTF-16BE";
                    break;
                default:
                    code = "0";
            }
        } catch (Exception e) {
            logger.error("codeString error!", e);
            throw new RuntimeException(e);
        } finally {
            if (bin != null) {
                try {
                    bin.close();
                } catch (IOException e) {
                    logger.error("codeString BufferedInputStream close() error!", e);
                    throw new RuntimeException(e);
                }
            }
        }

        return code;
    }

    /**
     * 系统路径
     */
    public static String getClassPath() {
        String classpath = "";
        try {
            String path = System.getProperty("user.dir");
            classpath = path;
            logger.info("getClassPath path: {}", classpath);

        } catch (Exception e) {
            logger.info("getClassPath error! ", e);
            throw new RuntimeException(e);
        }
        return classpath;
    }


    /**
     * 获取文件文件名不带后缀
     *
     * @param filepath filepath
     * @return String
     */
    public static String getFileName(String filepath) {
        int index = filepath.lastIndexOf(File.separator);

        if (index > -1) {
            filepath = filepath.substring(index + 1);
        }

        return filepath;
    }

    /**
     * 获取文件文件名不带后缀
     *
     * @param filepath filepath
     * @return String
     */
    public static String getFileNameNoSuffix(String filepath) {

        String filename = getFileName(filepath);
        int index = filename.lastIndexOf('.');

        if (index > -1) {
            filename = filename.substring(0, index);
        }

        return filename;
    }

    /**
     * 获取文件路径
     *
     * @param filepath filepath
     * @return String
     */
    public static String getFilePath(String filepath) {
        int index = filepath.lastIndexOf(File.separator);
        if (-1 != index){
            filepath = filepath.substring(0, index);
        }
        return filepath;
    }

    public static void main(String[] args) {
        try {
//            File file = new File("F:/tmp/0.wav");
//            System.out.println(FileBaseUtil.checkFileMagicByExt(file, "wav"));
//
//            file = new File("F:/tmp/日志信息.png");
//            System.out.println(FileBaseUtil.checkFileMagicByExt(file, "png,gif,jpg"));
//
//            file = new File("F:/tmp/111.gif");
//            System.out.println(FileBaseUtil.checkFileMagicByExt(file, "png,gif,jpg"));
//
//            file = new File("F:/tmp/111.jpg");
//            System.out.println(FileBaseUtil.checkFileMagicByExt(file, "png,gif,jpg"));
//
//            file = new File("F:/tmp/huangq.cer");
//            System.out.println(FileBaseUtil.checkFileMagicByExt(file, "cer"));

            readFileLines("D:\\workspacexunlian\\ZXPAY-XSMARTPAY-V1.03.X\\sourcenew\\service\\bank2\\bank2-webserver\\CTPTCNY000011011420170603");

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}
